// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// /“抽象”语法表示法。

package ir

import (
	"fmt"
	"go/constant"
	"sort"

	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/src"
)

// 节点是IR节点的抽象接口。
type Node interface {
	// 格式化
	Format(s fmt.State, verb rune)

	// 源位置。
	Pos() src.XPos
	SetPos(x src.XPos)

	// 用于制作副本。复制和复制。
	copy() Node

	doChildren(func(Node) bool) bool
	editChildren(func(Node) Node)

	// 抽象图结构，用于泛型遍历。
	Op() Op
	Init() Nodes

	// 仅特定于某些操作的字段。
	Type() *types.Type
	SetType(t *types.Type)
	Name() *Name
	Sym() *types.Sym
	Val() constant.Value
	SetVal(v constant.Value)

	// 分析通过的存储。
	Esc() uint16
	SetEsc(x uint16)
	Diag() bool
	SetDiag(x bool)

	// Typecheck值：
	// 0表示节点未进行类型检查
	// 1表示节点已完成类型检查
	// 2表示节点的类型检查正在进行
	// 3表示节点的类型来自types2，但可能需要转换
	Typecheck() uint8
	SetTypecheck(x uint8)
	NonNil() bool
	MarkNonNil()
}

// 行以字符串形式返回n的位置。如果n已内联，则
// 它使用n已内联的最外层位置。
func Line(n Node) string {
	return base.FmtPos(n.Pos())
}

func IsSynthetic(n Node) bool {
	name := n.Sym().Name
	return name[0] == '.' || name[0] == '~'
}

// IsAutoTmp表示编译器是否将n创建为临时的，
// 基于的设置。以n的名义自动标记。
func IsAutoTmp(n Node) bool {
	if n == nil || n.Op() != ONAME {
		return false
	}
	return n.Name().AutoTemp()
}

// MayBeShared报告是否在AST的多个位置出现n。
// 突变这样的节点时必须格外小心。
func MayBeShared(n Node) bool {
	switch n.Op() {
	case ONAME, OLITERAL, ONIL, OTYPE:
		return true
	}
	return false
}

type InitNode interface {
	Node
	PtrInit() *Nodes
	SetInit(x Nodes)
}

func TakeInit(n Node) Nodes {
	init := n.Init()
	if len(init) != 0 {
		n.(InitNode).SetInit(nil)
	}
	return init
}

// go:generate stringer-type=Op-trimprefix=O node。转到

type Op uint8

// 节点操作。
const (
	OXXX Op = iota

	// names 
	ONAME // var或func name 
	// 未命名的参数或返回值：f（int，string）（int，error）{etc}
	// 也用于尚未解析的限定包标识符。
	ONONAME
	OTYPE    // 类型名称
	OPACK    // 导入
	OLITERAL // 文字
	ONIL     // 无

	// 表达式
	OADD          // X+Y 
	OSUB          // X-Y 
	OOR           // X | Y 
	OXOR          // X^Y 
	OBYTES2STR    // 类型（X）（类型是字符串，X是[]字节）
	OBYTES2STRTMP // 类型（X）（类型是字符串，X是[]字节，短暂的）
	OSLICE2ARRPTR // 类型（X）（类型是*[N]T，X是[]T）
	// X=Y或（如果Def=true）X:=Y 
	OAS
	// Lhs=Rhs（X，Y，z=a，b，c）或（如果Def=true）Lhs:=Rhs 
	OAS2
	OAS2DOTTYPE // Lhs=Rhs（x，ok=I（int））
	OAS2FUNC    // Lhs=Rhs（x，y=f（））
	OAS2MAPR    // Lhs=Rhs（x，ok=m[“foo”]）
	OAS2RECV    // Lhs=Rhs（x，ok=<-c）

	// 在walk之前，它们是：X（Args），其中Args都是正则参数。
	// walk之后，如果任何参数的计算可能需要临时变量，
	// 该临时变量将被推送到Init，则Args将包含一组更新的
	// 参数。KeepAlive是连接到OCALLxxx的所有OVARLIVE节点。
	OCALLFUNC  // X（Args）（函数调用f（Args））
	OCALLMETH  // X（Args）（直接方法调用X.method（Args））
	OCALLINTER // X（Args）（接口方法调用X.method（Args））
	OCLOSURE   // func Type{func.Closure.Body}（func literal）
	OMAPLIT    // 类型{List}（复合文字，类型是map）
	OSTRUCTLIT // 类型{List}（复合文字，类型是struct）
	OARRAYLIT  // 类型{List}（复合文字，类型是数组）
	OSLICELIT  // 类型{List}（复合文字，类型是slice），Len是片长。
	OPTRLIT    // /&X（X是复合文字）
	OCONV      // 类型（X）（类型转换）
	OCONVIFACE // 类型（X）（类型转换，到接口）
	OCONVIDATA // 构建一个数据字来在接口中存储X。相当于IDATA（Confiface（X））。这是一个ir。ConvExpr。
	OCONVNOP   // Type（X）（类型转换，无效果）
	OCOPY      // copy（X，Y）
	ODCL       // var X（声明X.Type类型的X）

	// 在解析期间使用，但不会持续。
	ODCLFUNC  // func f（）或func（r）f（）
	ODCLCONST // const pi=3.14 
	ODCLTYPE  // type Int Int或type Int=Int 

	ODELETE        // delete（Args）
	ODOT           // X.Sel（X是结构类型）
	ODOTPTR        // X.Sel（X是指向结构类型的指针）
	ODOTMETH       // X.Sel（X是非接口，Sel是方法名称）
	ODOTINTER      // X.Sel（X是接口，Sel是方法名称）
	OXDOT          // X.Sel（在重写之前）
	ODOTTYPE       // X.Ntype或X.Type（.Ntype在解析期间，.Type一旦解析）；遍历之后，Itab包含接口类型描述符和Itab的地址。X包含具体类型描述符
	ODOTTYPE2      // X.Ntype或X.type（.Ntype在解析期间，.type一旦解析；在OAS2DOTTYPE的rhs上）；遍历后，Itab包含接口类型描述符
	OEQ            // X==Y 
	ONE            // X！=Y 
	OLT            // X<Y 
	OLE            // X<=Y 
	OGE            // X>=Y 
	OGT            // X>Y 
	ODEREF         // /*X 
	OINDEX         // X[索引]（数组或切片索引）
	OINDEXMAP      // X[索引]（映射索引）
	OKEY           // 键：值（键：结构/数组/映射文字中的值）
	OLEN           // len（X）
	OMAKE          // make（Args）（在类型检查转换为以下内容之一之前）
	OMAKECHAN      // make（type[，len]）（type is chan）
	OMAKEMAP       // make（type[，len]）（type is map）
	// omakeslicepcopy由订单pass创建，对应于：
	// s=make（type，Len）；当编译时Len==Len（Cap）已知时，可以在节点上设置copy（s，Cap）
	// 
	// Bounded。
	// 
	// 创建此节点是为了使步行通行证可以优化此模式，否则在订单通过后很难检测到。
	OMUL         // X*Y 
	ODIV         // X/Y 
	OMOD         // X%Y 
	OLSH         // X<Y 
	ORSH         // X>>Y 
	OAND         // X&Y 
	OANDNOT      // X和Y 
	ONEW         // 新（X）；对应于对源代码
	ONOT         // /中的new的调用！X 
	OBITNOT      // /^X 
	OPLUS        // /+X 
	ONEG         // /-X 
	OOROR        // X | | Y 
	OPANIC       // /恐慌（X）
	OPRINT       // 打印（列表）
	OPRINTN      // /打印LN（列表）
	OPAREN       // （X）
	OSEND        // /Chan
	OSLICESTR    // X[Low:High]（X为字符串）
	OSLICE3      // X[Low:High:Max]（X为非类型检查或切片）
	OSLICEHEADER // sliceheader{Ptr，Len Cap}（Ptr不安全。指针，Len是长度，Cap是容量）
	ORECV        // /<-X 
	ORUNESTR     // 类型（X）（类型是字符串，X是符文）
	OSELRECV2    // 类似于OAS2:Lhs=Rhs，其中len（Lhs）=2，len（Rhs）=1，Rhs[0]。Op=ORECV（显示为OCASE的.Var）
	OIOTA        // iota 
	OREAL        // real（X）
	OIMAG        // imag（X）
	OCOMPLEX     // complex（X，Y）
	OALIGNOF     // 。（X）
	OOFFSETOF    // 不安全。偏移量（X）
	OSIZEOF      // 不安全。Sizeof（X）
	OUNSAFEADD   // 不安全。添加（X，Y）
	OUNSAFESLICE // 不安全。切片（X，Y）
	OMETHEXPR    // X（Args）（方法表达式T.method（Args），第一个参数是方法接收者）
	OMETHVALUE   // X.Sel（方法表达式t.method，未调用）

	// statements 
	OBLOCK // {List}（代码块）
	OBREAK // break[Label]
	// OCASE:case List:Body（List==nil表示默认值）
	// ，列表是指定类型（或OLITERAL 
	// 表示nil）的OTYPE节点，或指示泛型的运行时类型的ODYNAMICTYPE。
	// 如果指定了类型开关变量，则Var是具有指定
	// 名称。
	// 类型的类型开关变量版本的
	OCASE
	OCONTINUE // 继续[Label]
	ODEFER    // 延迟调用
	OFALL     // 故障
	OFOR      // 用于初始化；康德；Post{Body}
	// OfRuntil与OFOR类似，但测试（Cond）在Body之后应用：
	// Init 
	// top:{Body}
	// cont cont cont:Post 
	// if Cond{
	OFORUNTIL
	OGOTO   // 转到标签
	OIF     // 如果初始化；Cond{Then}else{else}
	OLABEL  // Label:
	OGO     // 调用
	ORANGE  // 对于Key，Value=range X{Body}
	ORETURN // 返回结果
	OSELECT // 选择{Cases}
	OSWITCH // 开关初始化；Expr{Cases}
	// OTYPESW:X:=Y.（类型）（显示为OSWITCH的.Tag）
	// X为零，如果没有类型开关变量
	OTYPESW
	OFUNCINST // OTFUNC:func（）-Recv是receiver字段，Params是param字段列表，Results是

	// result字段列表。
	OTFUNC
	OTARRAY // /[8]整数或[…]int-
	OTSLICE // /[]int-

	// 其他
	// 内联调用的中间表示。对捕获的变量、参数、retvars和INLMARK op使用Init（赋值
	// ）、
	// Body（内联函数的Body）和ReturnVars（返回值列表
	// ）
	OINLCALL       // 内联调用的中间表示。
	OEFACE         // 接口值为空的itable和数据字。
	OITAB          // 接口值的可编辑字。
	OIDATA         // X中接口值的数据字
	OSPTR          // 片或字符串的基指针。
	OCFUNC         // 引用c函数指针（非go func值）
	OCHECKNIL      // 发出代码以确保指针/接口不为零
	OVARDEF        // 变量即将完全初始化
	OVARKILL       // 变量为死
	OVARLIVE       // 变量为活
	ORESULT        // 函数调用的结果；Xoffset是堆栈偏移量
	OINLMARK       // 内联体的开头，带有调用方的file/line。Xoffset是内联树的索引。
	OLINKSYMOFFSET // 名称内的偏移量

	// 泛型的操作码
	ODYNAMICDOTTYPE  // x=i.（T），其中T是类型参数（或从类型参数派生）
	ODYNAMICDOTTYPE2 // x，ok=i.（T），其中T是类型参数（或从类型参数派生）
	ODYNAMICTYPE     // 类型开关的类型节点（表示类型开关的动态目标类型）

	// 特定于搜索的操作码
	OTAILCALL    // 对另一个函数的尾部调用
	OGETG        // 运行时。getg（）（读取g指针）
	OGETCALLERPC // 运行时。getcallerpc（）（调用方框架中的继续PC）
	OGETCALLERSP // 运行时。getcallersp（）（调用方框架中的堆栈指针）

	OEND
)

// IsCmp报告op是否是比较操作（=，！=，<，<，<=，
// />，或>=）。
func (op Op) IsCmp() bool {
	switch op {
	case OEQ, ONE, OLT, OLE, OGT, OGE:
		return true
	}
	return false
}

// Nodes是指向*节点切片的指针。
// 对于大多数节点中未使用的字段，使用此字段代替
// 一个切片以节省空间。
type Nodes []Node

// Append将条目追加到节点。
func (n *Nodes) Append(a ...Node) {
	if len(a) == 0 {
		return
	}
	*n = append(*n, a...)
}

// 为节点预先添加条目。
// 如果传入了一个切片，它将拥有它的所有权。
func (n *Nodes) Prepend(a ...Node) {
	if len(a) == 0 {
		return
	}
	*n = append(a, *n...)
}

// Take clears n，返回以前的内容。
func (n *Nodes) Take() []Node {
	ret := *n
	*n = nil
	return ret
}

// Copy返回切片内容的副本。
func (n Nodes) Copy() Nodes {
	if n == nil {
		return nil
	}
	c := make(Nodes, len(n))
	copy(c, n)
	return c
}

// NameQueue是一个名为*的FIFO队列。NameQueue的零值是
// 一个随时可用的空队列。
type NameQueue struct {
	ring       []*Name
	head, tail int
}

// 空报告q是否不包含名称。
func (q *NameQueue) Empty() bool {
	return q.head == q.tail
}

// PushRight在队列右侧追加n。
func (q *NameQueue) PushRight(n *Name) {
	if len(q.ring) == 0 {
		q.ring = make([]*Name, 16)
	} else if q.head+len(q.ring) == q.tail {
		// 种植戒指。
		nring := make([]*Name, len(q.ring)*2)
		// 复制旧元素。
		part := q.ring[q.head%len(q.ring):]
		if q.tail-q.head <= len(part) {
			part = part[:q.tail-q.head]
			copy(nring, part)
		} else {
			pos := copy(nring, part)
			copy(nring[pos:], q.ring[:q.tail%len(q.ring)])
		}
		q.ring, q.head, q.tail = nring, 0, q.tail-q.head
	}

	q.ring[q.tail%len(q.ring)] = n
	q.tail++
}

// PopLeft从队列左侧弹出一个名称。如果q是
// 空的，它就会恐慌。
func (q *NameQueue) PopLeft() *Name {
	if q.Empty() {
		panic("dequeue empty")
	}
	n := q.ring[q.head%len(q.ring)]
	q.head++
	return n
}

// 名称集是一组名称。
type NameSet map[*Name]struct{}

// 有报告s是否包含n。
func (s NameSet) Has(n *Name) bool {
	_, isPresent := s[n]
	return isPresent
}

// Add将n添加到s。
func (s *NameSet) Add(n *Name) {
	if *s == nil {
		*s = make(map[*Name]struct{})
	}
	(*s)[n] = struct{}{}
}

// 排序返回s按较少排序。
func (s NameSet) Sorted(less func(*Name, *Name) bool) []*Name {
	var res []*Name
	for n := range s {
		res = append(res, n)
	}
	sort.Slice(res, func(i, j int) bool { return less(res[i], res[j]) })
	return res
}

type PragmaFlag uint16

const (
	// Func pragmas。
	Nointerface      PragmaFlag = 1 << iota
	Noescape                    // func参数不应转义
	Norace                      // func不能有种族检测器注释
	Nosplit                     // func不应在单独的堆栈上执行
	Noinline                    // func不应内联
	NoCheckPtr                  // func不应通过checkptr 
	CgoUnsafeArgs               // 将指向一个参数的指针视为指向所有参数的指针
	UintptrEscapes              // 转换为uintptr escape 

	// 仅限运行时的func杂注。
	// 见../..
	Systemstack        // func必须在系统堆栈上运行
	Nowritebarrier     // emit compiler error而不是write barrier 
	Nowritebarrierrec  // 在这个或递归被调用方中的write barrier错误
	Yeswritebarrierrec // 取消这个函数中的Nowritebarrierrec并调用

	// 运行时和cgo类型的pragmas 
	NotInHeap // 此类型的值不能由堆分配

	// Go命令pragmas 
	GoBuildPragma

	RegisterParams // TODO（寄存器参数）在寄存器abi工作后删除

)

func AsNode(n types.Object) Node {
	if n == nil {
		return nil
	}
	return n.(Node)
}

var BlankNode Node

func IsConst(n Node, ct constant.Kind) bool {
	return ConstType(n) == ct
}

// IsNil报告n是否代表通用的非类型化零值“nil”。
func IsNil(n Node) bool {
	// 检查n.因为常数传播可能会产生类型化的nil常量，所以
	// 在Go规范中不存在。
	return n != nil && Orig(n).Op() == ONIL
}

func IsBlank(n Node) bool {
	if n == nil {
		return false
	}
	return n.Sym().IsBlank()
}

// IsMethod报告n是否是一个方法。
// n必须是函数或方法。
func IsMethod(n Node) bool {
	return n.Type().Recv() != nil
}

func HasNamedResults(fn *Func) bool {
	typ := fn.Type()
	return typ.NumResults() > 0 && types.OrigSym(typ.Results().Field(0).Sym) != nil
}

// HasUniquePos报告n是否具有唯一的位置，可用于报告错误消息。
// 
// 主要用于区分对命名对象的引用，
// 其位置将指向它们的声明位置，而不是
// 它们的使用位置。
func HasUniquePos(n Node) bool {
	switch n.Op() {
	case ONAME, OPACK:
		return false
	case OLITERAL, ONIL, OTYPE:
		if n.Sym() != nil {
			return false
		}
	}

	if !n.Pos().IsKnown() {
		if base.Flag.K != 0 {
			base.Warn("setlineno: unknown position (line 0)")
		}
		return false
	}

	return true
}

func SetPos(n Node) src.XPos {
	lno := base.Pos
	if n != nil && HasUniquePos(n) {
		base.Pos = n.Pos()
	}
	return lno
}

// InitExpr的结果必须重新分配给n，例如
// n.X=InitExpr（init，n.X）
func InitExpr(init []Node, expr Node) Node {
	if len(init) == 0 {
		return expr
	}

	n, ok := expr.(InitNode)
	if !ok || MayBeShared(n) {
		// 引入OCONVNOP以保留init列表。
		n = NewConvExpr(base.Pos, OCONVNOP, nil, expr)
		n.SetType(expr.Type())
		n.SetTypecheck(1)
	}

	n.PtrInit().Prepend(init...)
	return n
}

// 写入n影响的外部值是什么？
// 外部值表示包含结构或数组。
func OuterValue(n Node) Node {
	for {
		switch nn := n; nn.Op() {
		case OXDOT:
			base.FatalfAt(n.Pos(), "OXDOT in OuterValue: %v", n)
		case ODOT:
			nn := nn.(*SelectorExpr)
			n = nn.X
			continue
		case OPAREN:
			nn := nn.(*ParenExpr)
			n = nn.X
			continue
		case OCONVNOP:
			nn := nn.(*ConvExpr)
			n = nn.X
			continue
		case OINDEX:
			nn := nn.(*IndexExpr)
			if nn.X.Type() == nil {
				base.Fatalf("OuterValue needs type for %v", nn.X)
			}
			if nn.X.Type().IsArray() {
				n = nn.X
				continue
			}
		}

		return n
	}
}

const (
	EscUnknown = iota
	EscNone    // 不转义到堆、结果或参数。
	EscHeap    // 可从堆中访问
	EscNever   // 通过构造将不会逃逸。
)
